Vlakna - zobrazeni informaci uzivateli na jeden form z vice vlaken

Otázka od: Ing. Jiri Sokol

19. 10. 2004 9:42

Ahoj!
Potrebuju nakopnout s viz subjekt. Ted to mam tak, ze hlavni aplikace vytvori
"informacni" formular, na kterym je ListView (aspon doufam, ze se nepletu -
proste pridavam zaznamy do tabulky se 3 sloupci).
Podle potreby tyto vlakna volaji jednu proceduru hlavniho formulare, ktera
zapise na ten infromacni fomrular pozadovane informace.
Tusim, ze toto neni spravna cesta. Automaticky me napada, ze bude problem nekde
se synchronizaci, ale posledne jste mi radili, abych se vyvaroval uziti
Synchronize ve vlaknech, ze pry jinak vlakna ani nemusim pouzivat. K priblizeni
co se deje - pouzivam workflow vlaken Petra Fejfara, akorat ne pres semaphor,
ale pres udalosti (nevznika mi fronta pozadavku). Tyto vlakna vytvorim jednou a
protoze si nactou nejake data z databaze, se kterymi pak pracuji, tak tyto
vlakna ziji po celou dobu zivota programu. Akorat vzdy provedou svoji akci a
pak se "uspi".
Jak to resite teda vy, kdyz mate vice vlaken a potebujete neco uzivateli
napsat?
Diky za rady
Jirka
--------------------------------------------------
Ing. Jiri Sokol; jiri.sokol@seznam.cz; 972 231 187
D6Prof+SP3; WinXPProf+SP1; FB 1.5.0
programator amater



Odpovedá: delphin@post.cz

19. 10. 2004 10:08

> Jak to resite teda vy, kdyz mate vice vlaken a potebujete neco uzivateli
napsat?

Napriklad poslat message hlavnimu vlaknu zpravu pomoci PostMessage.


Odpovedá: Milan Tomes

19. 10. 2004 10:02

Ahoj,

AFAIK tak jak to mas je v poradku a z principu je nutno vsechna volani VCL
synchronizovat prave pres Synchronize.
Hlavni thread aplikace by mel byt vyhrazen interakci s uzivatelem kam
zobrazeni ListView rozhodne spada a tak to mas IMHO naprosto spravne.
Je pravda, ze Synchronize zpusobi to, ze se dana metoda provede v kontextu
main threadu, ale to je presne to o co tady bezi. Jedine upozorneni je
takove, aby dana metoda delala jen a jen to pridani do ListView - tedy
zadnou narocnou akci. Synchronize totiz onen volajici thread pozastavi do
okamziku ukonceni volane metody.

Ja jsem toto vzdy resil pres volani nejake metody formulare pres
Synchronize, ktera si precetla hodnoty z property volajiciho threadu a
provedla akci (v Tvem pripade pridani hodnoty do ListView).

S pozdravem

Milan Tomes

> [mailto:delphi-l-owner@clexpert.cz]On Behalf Of Ing. Jiri Sokol
> Sent: Tuesday, October 19, 2004 10:33 AM
>
> Ahoj!
> Potrebuju nakopnout s viz subjekt. Ted to mam tak, ze hlavni
> aplikace vytvori "informacni" formular, na kterym je ListView
> (aspon doufam, ze se nepletu - proste pridavam zaznamy do tabulky
> se 3 sloupci).
> Podle potreby tyto vlakna volaji jednu proceduru hlavniho
> formulare, ktera zapise na ten infromacni fomrular pozadovane informace.
> Tusim, ze toto neni spravna cesta. Automaticky me napada, ze bude
> problem nekde se synchronizaci, ale posledne jste mi radili,
> abych se vyvaroval uziti Synchronize ve vlaknech, ze pry jinak
> vlakna ani nemusim pouzivat. K priblizeni co se deje - pouzivam


Odpovedá: Petr Fejfar

19. 10. 2004 9:55

Ing. Jiri Sokol wrote:

> problem nekde se synchronizaci, ale posledne jste mi radili, abych se
> vyvaroval uziti Synchronize ve vlaknech, ze pry jinak vlakna ani
> nemusim pouzivat.

Jenomze ty namitky se tykaly toho, ze jsi pomoci Synchronize spoustel cely
kod threadu - to ten thread pak skutecne k nicemu nepotrebujes.

Pokud je delka kodu provadeneho v ramci Synchronize rozumna a aplikaci
nevadi,
ze pokud se sejdou zadosti sychnronize, zastavi se v podstate vsechny
thready,
ktere synchronize volali, tak ji klidne volej, akorat se snaz omezit delku
kodu volaneho pres Synchronize na minimum.

Pokud to vadi nebo pokud se chces soustredit v budoucnu na propracovane
mutlithreaded aplikace, vyhodnejsi ale pracnejsi bude navrhnout si vlastni
IPC
(Inter Process Communication).


HTH, pf



Odpovedá: Milan Tomes

19. 10. 2004 10:46

Ano i to je moznost. Parametry se predaji v l(w)Param treba i pomoci nejake
dynamicke struktury, ale zalezi na casove narocnosti volane akce - aby
nebylo vytvareni a plneni struktury pomalejsi nez provedeni metody pomoci
Synchronize. Ale jde o princip.
Pokud bych mel framework, ktery je postaven na vlaknech, tak bych asi
opravdu volil tuto moznost uz je proto, ze je to *systemove*  

Ted jsem si uvedomil, ze to vlastne take tak delam a jako parametr predavam
pointer na dynamicky alokovany buffer se stringem. A tak bych se ted rad
zeptal ja:
1. Muze se stat, ze zprava neni zpracovana ???
2. Pokud ano, tak za jakych podminek ???
3. Pokud ano, tak kde mam zajistit korektni dealokaci pameti ???

Diky

S pozdravem

Milan Tomes

> [mailto:delphi-l-owner@clexpert.cz]On Behalf Of delphin@post.cz
> Sent: Tuesday, October 19, 2004 10:49 AM
>
> > Jak to resite teda vy, kdyz mate vice vlaken a potebujete neco uzivateli
> napsat?
>
> Napriklad poslat message hlavnimu vlaknu zpravu pomoci PostMessage.
>
>
>
>


Odpovedá: delphin@post.cz

19. 10. 2004 11:15

> Ted jsem si uvedomil, ze to vlastne take tak delam a jako parametr
predavam
> pointer na dynamicky alokovany buffer se stringem. A tak bych se ted rad
> zeptal ja:
> 1. Muze se stat, ze zprava neni zpracovana ???
> 2. Pokud ano, tak za jakych podminek ???
> 3. Pokud ano, tak kde mam zajistit korektni dealokaci pameti ???

Zpravy jsou zpracovavany dokud existuje prislusny formular a neni ukoncena
aplikace. Po ukonceni aplikace mohou nektere zpravy zustat nevyrizeny.


Odpovedá: Milan Tomes

19. 10. 2004 11:27

Dobre jak tedy provest korektni dealokaci v tomto pripade:

procedure TG3UDPServer.Execute;
var
  Sock: TUDPBlockSocket;
  Buf: string;
  DBConnection: TDBConnection;
  Buf_1: string;
  Ptr: Pointer;
begin
  SetName;
  if Assigned(FDBConnection) then
    DBConnection := TDBConnection(FDBConnection)
  else
    DBConnection := nil;
  Sock := TUDPBlockSocket.Create;
  try
    Sock.Bind(cAnyHost, IntToStr(FPort));
    if Sock.LastError <> 0 then
      exit;
    while True do
    begin
      if Terminated then
        break;
      Buf := sock.RecvTerminated(1000, cmd_Terminator) + cmd_Terminator;
      if Sock.LastError = 0 then
      begin
        if Buf = cmd_GetIP then
        begin
          Sock.SendString(cmd_GetIP_Response);
        end;
        if Buf = cmd_AppParamsChanged then
          PostMessage(Application.MainForm.Handle, WM_PARAMSCHANGED, 0, 0);
        if copy(Buf, 1, Length(cmd_FunctionParamsChanged)) =
cmd_FunctionParamsChanged then
        begin
          if Length(Buf) = Length(cmd_FunctionParamsChanged) +
Length(cmd_Terminator) then
            PostMessage(Application.MainForm.Handle, WM_PARAMSCHANGED,
1, -1)
          else
            PostMessage(Application.MainForm.Handle, WM_PARAMSCHANGED, 1,
StrToInt(copy(Buf, Length(cmd_FunctionParamsChanged) + 2, Length(Buf) -
Length(cmd_FunctionParamsChanged) - 2)));
        end;
        if Buf = cmd_ShutDownApp then
          PostMessage(Application.MainForm.Handle, WM_CLOSE, 1, 0);
        if copy(Buf, 1, Length(cmd_AdminMessage)) = cmd_AdminMessage then
        begin
          Buf_1 := copy(Buf, Length(cmd_AdminMessage) + 2, Length(Buf) -
Length(cmd_AdminMessage) - 2);
          GetMem(Ptr, Length(Buf_1) + 1);
          StrPCopy(PChar(Ptr), Buf_1);
          PostMessage(Application.MainForm.Handle, WM_ADMINMESSAGE,
Longint(Ptr), 0);
        end;
        if copy(Buf, 1, Length(cmd_UserMessage)) = cmd_UserMessage then
        begin
          Buf_1 := copy(Buf, Length(cmd_UserMessage) + 2, Length(Buf) -
Length(cmd_UserMessage) - 2);
          GetMem(Ptr, Length(Buf_1) + 1);
          StrPCopy(PChar(Ptr), Buf_1);
          PostMessage(Application.MainForm.Handle, WM_USERMESSAGE,
Longint(Ptr), 0);
        end;
      end;
      Sleep(1);
    end;
    Sock.CloseSocket;
  finally
    freeAndNil(Sock);
  end;
end;

Jedna se o vlakno, ktere ma otevreny konkretni UDP port pro prichozi
pozadavky. Na nektere pozadavky posila v ramci daneho spojeni odpoved a na
nektere ne - jen zasle zpravu hlavnimu oknu aplikace zpravu.
Toto okno ji zpracuje nasledovne:

procedure TG3WMainForm.WndProc(var Message: TMessage);
var
  P: PChar;
  i: integer;
begin
  if (Message.Msg = WM_CLOSE) and (Message.WParam = 1) then
    Application.Terminate;
  if Message.Msg = WM_PARAMSCHANGED then
  begin
    case Message.WParam of
      0: Gor3WinApp.Params.LoadParams([ptApp]);
      1: Gor3WinApp.Params.LoadParams([ptFunction]);
    end;
    for i := 0 to Pred(Screen.FormCount) do
      if (Screen.Forms[i] <> self) and (Screen.Forms[i] is TG3WForm) then
        SendMessage(Screen.Forms[i].Handle, WM_PARAMSCHANGED,
Message.wParam, Message.lParam);
  end;
  if Message.Msg = WM_ADMINMESSAGE then
  begin
    P := StrNew(PChar(Message.wParam));
    try
      FreeMem(Pointer(Message.wParam), Length(P) + 1);
      if IsIconic(Application.Handle) then
        FlashWindow(Application.Handle, true);
      Application.MessageBox(PChar(HexStrToStr(P)), 'Zprava administratora',
MB_OK + MB_ICONINFORMATION);
    finally
      StrDispose(P);
    end;
  end;
  if Message.Msg = WM_USERMESSAGE then
  begin
    P := StrNew(PChar(Message.wParam));
    try
      FreeMem(Pointer(Message.wParam), Length(P) + 1);
      UserMessage(HexStrToStr(P));
    finally
      StrDispose(P);
    end;
  end;
  inherited;
end;

V pripade kdy dotycna zprava nedojde, tak samozrejme nedojde k uvolneni
alokovane pameti. Vim, ze v pripade ukonceni aplikace dojde i k uvolneni
veskere pameti procesu, kde byla dotycna pamet alokovana, ale preci jen se
mi to vubec nelibi... Jakym zpusobem lze tedy predavat dynamicky alokovane
parametry a korektne je uvolnovat ???

S pozdravem

Milan Tomes

> [mailto:delphi-l-owner@clexpert.cz]On Behalf Of delphin@post.cz
> Sent: Tuesday, October 19, 2004 12:16 PM
>
> > Ted jsem si uvedomil, ze to vlastne take tak delam a jako parametr
> predavam
> > pointer na dynamicky alokovany buffer se stringem. A tak bych se ted rad
> > zeptal ja:
> > 1. Muze se stat, ze zprava neni zpracovana ???
> > 2. Pokud ano, tak za jakych podminek ???
> > 3. Pokud ano, tak kde mam zajistit korektni dealokaci pameti ???
>
> Zpravy jsou zpracovavany dokud existuje prislusny formular a neni ukoncena
> aplikace. Po ukonceni aplikace mohou nektere zpravy zustat nevyrizeny.


Odpovedá: delphin@post.cz

19. 10. 2004 11:53

> V pripade kdy dotycna zprava nedojde, tak samozrejme nedojde k uvolneni
> alokovane pameti. Vim, ze v pripade ukonceni aplikace dojde i k uvolneni
> veskere pameti procesu, kde byla dotycna pamet alokovana, ale preci jen se
> mi to vubec nelibi... Jakym zpusobem lze tedy predavat dynamicky alokovane
> parametry a korektne je uvolnovat ???

V takovem pripade je nutne parametry nekde udrzovat. Resenim je napriklad
predavat je pres TThreadList a hlavnimu vlaknu pote posilat univerzalni
zpravu a pripravenosti konkretni zpravy v TThreadListu. Pri ukonceni
aplikace neni pote problem nevyrizene zpravy korektne uvolnit.